package com.fingard.dsp.bank.directbank.weixin01;

import com.fingard.FGBiz;
import com.fingard.common.utils.json.JSONUtils;
import com.fingard.constant.Format;
import com.fingard.crypto.DigestHelper;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.weixin01.domain.DomainDetection;
import com.fingard.dsp.bank.directbank.weixin01.domain.IDomainService;
import com.fingard.dsp.bank.directbank.weixin01.domain.SuccessRateCount;
import com.fingard.dsp.bank.directbank.weixin01.domain.common.DomainConfig;
import com.fingard.dsp.bank.directbank.weixin01.domain.common.DomainConstants;
import com.fingard.io.FileHelper;
import com.fingard.net.ConnState;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;
import com.fingard.text.UrlHelper;
import com.fingard.util.DateHelper;
import com.fingard.util.SortHelper;
import com.fingard.xml.XElement;
import com.fingard.xml.XmlTextReader;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

@SuppressWarnings("deprecation")
public class WEIXIN01Base extends DirectBase {
    protected String getCharset() {
        return getBankConfig().getCharset("UTF-8");
    }

    protected String get_nonce_str() throws Exception {
        String time_stamp = DateHelper.getSecsFrom1970();
        //随机字符串 String(32)
        Random random = new Random();
        long randLong = random.nextLong();
        randLong = random.nextLong();
        String nonce_str = time_stamp + String.format("%20d", randLong);
        nonce_str = DigestHelper.cryptoMD5RetHexStr(nonce_str.getBytes());
        return nonce_str;
    }

    public String signMessageArray(String[] p_params, String p_saltKey, String signType) throws Exception {
        StringBuilder sbValue = new StringBuilder();

        String[] sortedValues = SortHelper.BubbleSort(p_params);
        for (int i = 0; i < sortedValues.length; i++) {
            if (i > 0) {
                sbValue.append("&");
            }
            sbValue.append(sortedValues[i]);
        }
        sbValue.append("&key=" + p_saltKey);
        WriteBankLogLn2("签约原始数据:\r\n" + sbValue.toString());

        String sign = "";
        if ("md5".equalsIgnoreCase(signType)) {
            byte[] tmpDataBytes = sbValue.toString().getBytes(getCharset());
            sign = DigestHelper.cryptoMD5RetHexStr(tmpDataBytes).toUpperCase();
        } else {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(p_saltKey.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] array = sha256_HMAC.doFinal(sbValue.toString().getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            sign = sb.toString().toUpperCase();
        }
        return sign;
    }

    public String signMessage(String[] p_params, String p_saltKey) throws Exception {
        String[] sortedValues = SortHelper.BubbleSort(p_params);
        StringBuilder sbValues = new StringBuilder();
        for (int i = 0; i < sortedValues.length; i++) {
            if (i > 0) {
                sbValues.append("&");
            }
            sbValues.append(sortedValues[i]);
        }
        sbValues.append("&key=" + p_saltKey);
        WriteBankLogLn2("签约原始数据:\r\n" + sbValues.toString());
        byte[] tmpDataBytes = sbValues.toString().getBytes(getCharset());
        return DigestHelper.cryptoMD5RetHexStr(tmpDataBytes).toUpperCase();
    }

    public boolean verifySign(String p_xml, String p_saltKey, StringBuilder p_sb) throws Exception {
        XmlTextReader tmpBkReader = loadBankRespXmlReader(p_xml, p_sb);
        if (tmpBkReader != null) {
            if (tmpBkReader.read("/xml")) {
                ArrayList<XElement> tmpList = tmpBkReader.readedXElement.getAllChildList();
                if (tmpList != null && tmpList.size() > 0) {
                    String chkSign = "";

                    ArrayList<String> arrayParams = new ArrayList<String>();
                    for (int i = 0; i < tmpList.size(); i++) {
                        XElement xe = tmpList.get(i);
                        if (xe.name.equals("sign")) {
                            chkSign = xe.innerText;
                        } else if (StringHelper.hasAnyChar(xe.innerText)) {
                            arrayParams.add(xe.name + "=" + xe.innerText);
                        }
                    }

                    String[] signParams = new String[arrayParams.size()];
                    arrayParams.toArray(signParams);
                    String sign = this.signMessage(signParams, p_saltKey);
                    if (sign.equals(chkSign)) {
                        return true;
                    } else {
                        p_sb.append("验签失败");
                        return false;
                    }
                } else {
                    p_sb.append("用于验签的xml节点为空");
                    return false;
                }
            } else {
                p_sb.append("用于验签的xml节点为空");
                return false;
            }
        } else {
            return false;
        }
    }

    protected String[] sendToBank(String p_reqStr, String p_for, boolean isDisaster) throws Exception {
        String[] retStr = new String[]{"", ""};

        String url = bankFront.getUrl(p_for);
        WriteBankUrlLogLn(url);

        boolean useDisasterResist = getBankConfig().getValueAsBool("useDisasterResist");
        useDisasterResist = isDisaster;
        WriteBankLogLn("微信通讯是否启用冗灾方案:" + useDisasterResist);

        String programme = getBankConfig().getValueAsString("programmeEnum");
        WriteBankLogLn2("微信冗灾采用方案:" + programme);

        IDomainService domainService = null;
        String domain = "";
        if (useDisasterResist) {
            domainService = disasterResist(programme);
            if (url.contains("api.mch.weixin.qq.com")) {
                domain = domainService.getDomain(DomainConstants.DomainType.API_MCH);
            } else if (url.contains("api.weixin.qq.com")) {
                domain = domainService.getDomain(DomainConstants.DomainType.API);
            }
            WriteBankLogLn("获取当前微信支付域名:" + domain);
            url = UrlHelper.replaceUrlDomain(url, domain);
            WriteBankLogLn("冗灾切换后发送微信地址:" + url);
        }

        WriteBankLogStep2(p_reqStr);
        WebRequest tmpWebRequest = new WebRequest(url);
        int timeout = 10000;
        if ("downloadbill".equals(p_for)) {
            timeout = getReadTimeout(60);
        } else {
            timeout = getReadTimeout(15);
        }
        //tmpWebRequest.setConnectTimeout(60000);
        tmpWebRequest.setReadTimeout(timeout);
        tmpWebRequest.setRequestProperty("connection", "Close");
        byte[] tmpUpBytes = p_reqStr.getBytes(getCharset());
        retStr = tmpWebRequest.upload(tmpUpBytes, getCharset());

        if (useDisasterResist) {
            if (retStr[0].length() == 0) {
                if ("A".equals(programme)) {
                    domainService.reportResult(DomainConstants.ReportResult.SUCCESS, domain);
                }
            }
            if (ConnState.FAIL.equals(retStr[0]) || (retStr[1] != null && retStr[1].contains("Connection reset"))) {
                if ("A".equals(programme)) {
                    domainService.reportResult(DomainConstants.ReportResult.FAIL, domain);
                }
                domain = domainService.getInverseDomain(domain);
                url = UrlHelper.replaceUrlDomain(url, domain);
                WriteBankLogLn("当前通讯异常码:" + retStr[0] + ",通讯异常冗灾切换后发送微信地址:" + url);

                tmpWebRequest = new WebRequest(url);
                tmpWebRequest.setReadTimeout(timeout);
                tmpWebRequest.setRequestProperty("connection", "Close");
                retStr = tmpWebRequest.upload(tmpUpBytes, getCharset());
            }
        }
        WriteBankLogStep3(retStr[1]);
        if (retStr[0].length() == 0) {
            int tmpIndex = retStr[1].indexOf("<");
            if (tmpIndex > 0) {
                retStr[1] = retStr[1].substring(tmpIndex);
            }
        } else {
            this.WriteBankLogLn(tmpWebRequest.sbLog);
        }
        return retStr;
    }

    /**
     * 初始化跨城冗灾对象
     *
     * @param programme
     * @return
     */
    protected IDomainService disasterResist(String programme) {
        IDomainService domainService = null;
        DomainConfig conf = null;

        if ("A".equals(programme)) {
            String reserveDomainReferRate = getBankConfig().getSetValue("reserveDomainReferRate", "10", "A");
            String resultReportFrequency = getBankConfig().getSetValue("resultReportFrequency", "60", "A");
            String resultDataMaxSize = getBankConfig().getSetValue("resultDataMaxSize", "100", "A");
            String reportPassingRate = getBankConfig().getSetValue("reportPassingRate", "85", "A");
            conf = new DomainConfig.Builder()
                    //设置主备域名流量分配情况,例如value=2，表示0.02%的流量走备用域名，99.98%的流量走主域名，用户可根据自身业务流量状况传递1-10000之间的数值。
                    .reserveDomainReferRate(Integer.parseInt(reserveDomainReferRate))
                    //设置定时器 多久执行一次上报结果分析策略计算逻辑，例如value=10000，表示10秒执行一次，用户可根据自身业务状况传递大于等于10000的数值（单位毫秒）。
                    .resultReportFrequency(Integer.parseInt(resultReportFrequency) * 1000)
                    //设置请求结果上报数据存储器最大能保留的数据量，例如value=100，表示上报数据最多保留100条，当数据满足100条后再次上报会清除最早的数据使数据存储器不会超过100的限制，用户可根据自身业务状况传递大于50的数值。
                    .resultDataMaxSize(Integer.parseInt(resultDataMaxSize))
                    //主域名上报结果成功通过率,例如value=90，表示主域名通过百分率只有在大于等于90%的情况下才会切换为主域名、小于会切换备用域名，用户可根据自身业务状况传递80-100的数值。
                    .reportPassingRate(Integer.parseInt(reportPassingRate))
                    .build();
            domainService = SuccessRateCount.getInstance(conf);
        } else if ("B".equals(programme)) {
            //设置主备域名流量分配情况,例如value=2，表示0.02%的流量走备用域名，99.98%的流量走主域名，用户可根据自身业务流量状况传递1-10000之间的数值。
            String reserveDomainReferRate = getBankConfig().getSetValue("reserveDomainReferRate", "10", "B");
            //定时器-探测主域名连通性执行频率,设置定时器多久探测主域名连通性逻辑，例如value=10，表示10秒执行一次，用户可根据自身业务状况传递大于等于10数值（单位秒）。
            String domainProbeFrequency = getBankConfig().getSetValue("domainProbeFrequency", "60", "B");

            conf = new DomainConfig.Builder()
                    .reserveDomainReferRate(Integer.parseInt(reserveDomainReferRate))
                    .domainProbeFrequency(Integer.parseInt(domainProbeFrequency) * 1000)
                    .build();
            domainService = DomainDetection.getInstance(conf);
        }

        return domainService;
    }

    protected String[] sendToBank(String p_reqStr, String p_for) throws Exception {
        String[] retStr = new String[]{"", ""};

        String url = "";
        if (p_for.contains("?")) {
            url = p_for;
        } else {
            url = bankFront.getUrl(p_for);
        }
        WriteBankUrlLogLn(url);

        boolean useDisasterResist = getBankConfig().getValueAsBool("useDisasterResist");
        WriteBankLogLn("微信通讯是否启用冗灾方案:" + useDisasterResist);

        String programme = getBankConfig().getValueAsString("programmeEnum");
        WriteBankLogLn2("微信冗灾采用方案:" + programme);

        IDomainService domainService = null;
        String domain = "";
        if (useDisasterResist) {
            domainService = disasterResist(programme);
            if (url.contains("api.mch.weixin.qq.com")) {
                domain = domainService.getDomain(DomainConstants.DomainType.API_MCH);
            } else if (url.contains("api.weixin.qq.com")) {
                domain = domainService.getDomain(DomainConstants.DomainType.API);
            }
            WriteBankLogLn("获取当前微信支付域名:" + domain);
            url = UrlHelper.replaceUrlDomain(url, domain);
            WriteBankLogLn("冗灾切换后发送微信地址:" + url);
        }


        WebRequest tmpWebRequest = new WebRequest(url);
        int timeout = 10000;
        if (url.contains("downloadbill")) {
            timeout = getReadTimeout(60);
        } else {
            timeout = getReadTimeout(15);
        }
        tmpWebRequest.setReadTimeout(timeout);

        tmpWebRequest.setRequestProperty("connection", "Close");

        byte[] tmpUpBytes = null;
        if (!p_for.contains("?")) {
            WriteBankLogStep2(p_reqStr);
            tmpUpBytes = p_reqStr.getBytes(getCharset());
        }
        retStr = tmpWebRequest.upload(tmpUpBytes, getCharset());

        if (useDisasterResist) {
            if (retStr[0].length() == 0) {
                if ("A".equals(programme)) {
                    domainService.reportResult(DomainConstants.ReportResult.SUCCESS, domain);
                }
            }
            if (ConnState.FAIL.equals(retStr[0]) || (retStr[1] != null && retStr[1].contains("Connection reset"))) {
                if ("A".equals(programme)) {
                    domainService.reportResult(DomainConstants.ReportResult.FAIL, domain);
                }
                domain = domainService.getInverseDomain(domain);
                url = UrlHelper.replaceUrlDomain(url, domain);
                WriteBankLogLn("当前通讯异常码:" + retStr[0] + ",通讯异常冗灾切换后发送微信地址:" + url);

                tmpWebRequest = new WebRequest(url);
                tmpWebRequest.setReadTimeout(timeout);
                tmpWebRequest.setRequestProperty("connection", "Close");
                retStr = tmpWebRequest.upload(tmpUpBytes, getCharset());
            }
        }
        WriteBankLogStep3(retStr[1]);
        if (retStr[0].length() == 0) {
            int tmpIndex = retStr[1].indexOf("<");
            if (tmpIndex > 0) {
                retStr[1] = retStr[1].substring(tmpIndex);
            }
        } else {
            this.WriteBankLogLn(tmpWebRequest.sbLog);
        }
        return retStr;
    }

    protected String[] sendToBank(String p_reqStr, String p_for, ActSetItem actItem) throws Exception {
        String[] retStr = new String[]{"", ""};

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File(actItem.ownCertFilePath));
        keyStore.load(instream, actItem.ownKeyPassword.toCharArray());

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, actItem.ownKeyPassword.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"},
                null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        CloseableHttpResponse response = null;
        try {
            String url = bankFront.getUrl(p_for);
            WriteBankUrlLogLn(url);

            boolean useDisasterResist = getBankConfig().getValueAsBool("useDisasterResist");
            WriteBankLogLn("微信通讯是否启用冗灾方案:" + useDisasterResist);

            String programme = getBankConfig().getValueAsString("programmeEnum");
            WriteBankLogLn2("微信冗灾采用方案:" + programme);

            IDomainService domainService = null;
            String domain = "";
            if (useDisasterResist) {
                domainService = disasterResist(programme);
                if (url.contains("api.mch.weixin.qq.com")) {
                    domain = domainService.getDomain(DomainConstants.DomainType.API_MCH);
                } else if (url.contains("api.weixin.qq.com")) {
                    domain = domainService.getDomain(DomainConstants.DomainType.API);
                }
                WriteBankLogLn("获取当前微信支付域名:" + domain);

                url = UrlHelper.replaceUrlDomain(url, domain);
                WriteBankLogLn("冗灾切换后发送微信地址:" + url);
            }
            WriteBankLogStep2(p_reqStr);

            HttpPost httpost = new HttpPost(url);
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(p_reqStr, getCharset()));

            RequestConfig.Builder customReq = RequestConfig.custom();
            customReq.setConnectTimeout(10000);
            customReq.setSocketTimeout(10000);
            boolean enableProxy = FGBiz.dspCenter.dspSet.enableProxy;
            if (enableProxy) {
                String proxyIP = FGBiz.dspCenter.dspSet.proxyIP;
                int proxyPort = Integer.parseInt(FGBiz.dspCenter.dspSet.proxyPort);
                HttpHost proxy = new HttpHost(proxyIP, proxyPort,"http");
                customReq.setProxy(proxy);
            }
            httpost.setConfig(customReq.build());

            response = httpclient.execute(httpost);
            HttpEntity entity = response.getEntity();
            String tmpRetStr = EntityUtils.toString(response.getEntity(), getCharset());

            if (useDisasterResist) {
                if (retStr[0].length() == 0) {
                    if ("A".equals(programme)) {
                        domainService.reportResult(DomainConstants.ReportResult.SUCCESS, domain);
                    }
                }
                if (StringHelper.isNullOrEmpty(tmpRetStr)) {
                    if ("A".equals(programme)) {
                        domainService.reportResult(DomainConstants.ReportResult.FAIL, domain);
                    }
                    domain = domainService.getInverseDomain(domain);
                    url = UrlHelper.replaceUrlDomain(url, domain);
                    WriteBankLogLn("当前通讯异常码:" + retStr[0] + ",通讯异常冗灾切换后发送微信地址:" + url);

                    httpost = new HttpPost(url);
                    httpost.addHeader("Connection", "keep-alive");
                    httpost.addHeader("Accept", "*/*");
                    httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
                    httpost.addHeader("Host", "api.mch.weixin.qq.com");
                    httpost.addHeader("X-Requested-With", "XMLHttpRequest");
                    httpost.addHeader("Cache-Control", "max-age=0");
                    httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
                    httpost.setEntity(new StringEntity(p_reqStr, getCharset()));
                    entity = httpclient.execute(httpost).getEntity();
                    tmpRetStr = EntityUtils.toString(response.getEntity(), getCharset());
                }
            }
            WriteBankLogStep3(tmpRetStr);
            EntityUtils.consume(entity);
            retStr[0] = "";
            retStr[1] = tmpRetStr;
        } catch (Exception ex) {
            ex.printStackTrace();
            retStr[0] = "-1";
            retStr[1] = ex.getMessage();
            WriteBankLogLn(ex);
        } finally {
            httpclient.close();
            response.close();
        }

        return retStr;
    }

    public HashMap<String, String> getOpenid(String p_appId, String p_appSecret, String p_code) {
        HashMap<String, String> retHash = new HashMap<String, String>();

        String respCode = "";
        String respInfo = "";

        String tmpUrl = bankFront.getUrl("access_token");
        if (StringHelper.isNullOrEmpty(tmpUrl)) {
            respCode = "-1";
            respInfo = "未设置“通过code换取网页授权access_token”的地址,请检查BankFront.xml相关配置!";
        } else {
            tmpUrl = tmpUrl + "?appid=" + p_appId + "&secret=" + p_appSecret + "&code=" + p_code
                    + "&grant_type=authorization_code";
            WriteBankLogStep2(tmpUrl);

            boolean useDisasterResist = getBankConfig().getValueAsBool("useDisasterResist");
            WriteBankLogLn("微信通讯是否启用冗灾方案:" + useDisasterResist);

            String programme = getBankConfig().getValueAsString("programmeEnum");
            WriteBankLogLn2("微信冗灾采用方案:" + programme);

            IDomainService domainService = null;
            String domain = "";
            if (useDisasterResist) {
                domainService = disasterResist(programme);
                if (tmpUrl.contains("api.mch.weixin.qq.com")) {
                    domain = domainService.getDomain(DomainConstants.DomainType.API_MCH);
                } else if (tmpUrl.contains("api.weixin.qq.com")) {
                    domain = domainService.getDomain(DomainConstants.DomainType.API);
                }
                WriteBankLogLn("获取当前微信支付域名:" + domain);

                tmpUrl = UrlHelper.replaceUrlDomain(tmpUrl, domain);
                WriteBankLogLn("冗灾切换后发送微信地址:" + tmpUrl);
            }

            WebRequest tmpWebRequest = new WebRequest(tmpUrl);
            String[] tmpRet = tmpWebRequest.upload(null, getCharset());

            if (useDisasterResist) {
                if (tmpRet[0].length() == 0) {
                    if ("A".equals(programme)) {
                        domainService.reportResult(DomainConstants.ReportResult.SUCCESS, domain);
                    }
                }
                if (ConnState.FAIL.equals(tmpRet[0]) || (tmpRet[1] != null && tmpRet[1].contains("Connection reset"))) {
                    if ("A".equals(programme)) {
                        domainService.reportResult(DomainConstants.ReportResult.FAIL, domain);
                    }
                    domain = domainService.getInverseDomain(domain);
                    tmpUrl = UrlHelper.replaceUrlDomain(tmpUrl, domain);
                    WriteBankLogLn("当前通讯异常码:" + tmpRet[0] + ",通讯异常冗灾切换后发送微信地址:" + tmpUrl);

                    tmpWebRequest = new WebRequest(tmpUrl);
                    tmpRet = tmpWebRequest.upload(null, getCharset());
                }
            }
            if (tmpRet[0].length() == 0) {
                WriteBankLogStep3(tmpRet[1]);

                if (tmpRet[1].startsWith("{")) {
                    retHash = JSONUtils.json2Map(tmpRet[1]);
                    respCode = "0";
                } else {
                    respCode = "-1";
                    respInfo = tmpRet[1];
                }
            } else {
                respCode = "-1";
                respInfo = tmpRet[1];
                WriteBankLogLn(tmpRet[0] + "\r\n" + tmpRet[1]);
            }
        }

        retHash.put("RespCode", respCode);
        retHash.put("RespInfo", respInfo);

        return retHash;
    }

    public HashMap<String, String> freshAccessToken(String p_appId, String p_appSecret) {
        HashMap<String, String> retHash = new HashMap<String, String>();

        String respCode = "";
        String respInfo = "";

        String tmpUrl = bankFront.getUrl("get_access_token");

        if (StringHelper.isNullOrEmpty(tmpUrl)) {
            respCode = "-1";
            respInfo = "未设置“通过appid获取access_token”的地址,请检查BankFront.xml相关配置!";
        } else {
            tmpUrl = tmpUrl + "?appid=" + p_appId + "&secret=" + p_appSecret + "&grant_type=client_credential";
            WriteBankLogStep2(tmpUrl);

            boolean useDisasterResist = getBankConfig().getValueAsBool("useDisasterResist");
            WriteBankLogLn("微信通讯是否启用冗灾方案:" + useDisasterResist);

            String programme = getBankConfig().getValueAsString("programmeEnum");
            WriteBankLogLn2("微信冗灾采用方案:" + programme);

            IDomainService domainService = null;
            String domain = "";
            if (useDisasterResist) {
                domainService = disasterResist(programme);
                if (tmpUrl.contains("api.mch.weixin.qq.com")) {
                    domain = domainService.getDomain(DomainConstants.DomainType.API_MCH);
                } else if (tmpUrl.contains("api.weixin.qq.com")) {
                    domain = domainService.getDomain(DomainConstants.DomainType.API);
                }
                WriteBankLogLn("获取当前微信支付域名:" + domain);

                tmpUrl = UrlHelper.replaceUrlDomain(tmpUrl, domain);
                WriteBankLogLn("冗灾切换后发送微信地址:" + tmpUrl);
            }

            WebRequest tmpWebRequest = new WebRequest(tmpUrl);
            String[] tmpRet = tmpWebRequest.upload(null, getCharset());

            if (useDisasterResist) {
                if (tmpRet[0].length() == 0) {
                    if ("A".equals(programme)) {
                        domainService.reportResult(DomainConstants.ReportResult.SUCCESS, domain);
                    }
                }
                if (ConnState.FAIL.equals(tmpRet[0]) || (tmpRet[1] != null && tmpRet[1].contains("Connection reset"))) {
                    if ("A".equals(programme)) {
                        domainService.reportResult(DomainConstants.ReportResult.FAIL, domain);
                    }
                    domain = domainService.getInverseDomain(domain);
                    tmpUrl = UrlHelper.replaceUrlDomain(tmpUrl, domain);
                    WriteBankLogLn("当前通讯异常码:" + tmpRet[0] + ",通讯异常冗灾切换后发送微信地址:" + tmpUrl);

                    tmpWebRequest = new WebRequest(tmpUrl);
                    tmpRet = tmpWebRequest.upload(null, getCharset());
                }
            }
            if (tmpRet[0].length() == 0) {
                WriteBankLogStep3(tmpRet[1]);

                if (tmpRet[1].startsWith("{")) {
                    retHash = JSONUtils.json2Map(tmpRet[1]);
                    respCode = "0";
                } else {
                    respCode = "-1";
                    respInfo = tmpRet[1];
                }
            } else {
                respCode = "-1";
                respInfo = tmpRet[1];
                WriteBankLogLn(tmpRet[0] + "\r\n" + tmpRet[1]);
            }
        }

        retHash.put("RespCode", respCode);
        retHash.put("RespInfo", respInfo);

        return retHash;
    }

    public String[] getAccessToken(String p_appId, String p_appSecret) {

        String[] tmpRet = new String[]{"", ""};
        //获取到的凭证
        String accessToken = "";
        //	凭证有效时间，单位：秒
        String expiresIn = "";
        boolean isNeedFetchToken = true;

//        String tokenDir = getBankConfig().getValueAsString("TokenDir");
//
//        if (tokenDir == null || "".equals(tokenDir)) {
//            tmpRet[0] = "-1";
//            tmpRet[1] = "config.xml文件 未设置TokenDir参数 !";
//            return tmpRet;
//        }
        //tokenArray [过期日期,AccessToken]
        Object[] tokenArray = bankFront.getToken(p_appId);
        do {
            if (tokenArray != null) {
                Date deadline = (Date) tokenArray[0];
                if (deadline.getTime() > System.currentTimeMillis()) {
                    isNeedFetchToken = false;
                    tmpRet[1] = tokenArray[1].toString();
                }
            }
            if (bankFront.isGettingToken) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    WriteBankLogLn(e);
                }
            }
        } while (bankFront.isGettingToken);


        if (isNeedFetchToken) {
            try {
                //防止多个地方重复获取Token
                bankFront.isGettingToken = true;
                WriteBankLogLn("开始重新获取token");
                Map<String, String> accessTokenMap = freshAccessToken(p_appId, p_appSecret);
                if ("0".equals(accessTokenMap.get("RespCode"))) {
                    if (accessTokenMap.containsKey("access_token")) {
                        accessToken = accessTokenMap.get("access_token");
                        expiresIn = accessTokenMap.get("expires_in");
                        Long passtime = Long.parseLong(expiresIn) * 1000;
                        Date date = new Date(System.currentTimeMillis() + passtime);
                        tokenArray = new Object[]{date, accessToken};
                        bankFront.setToken(p_appId, tokenArray);
                        WriteBankLogLn("更新token到期时间为：" + Format.DateTime14Format.format(date));
                        tmpRet[1] = accessToken;
                    } else {
                        tmpRet[0] = "-1";
                        String errcode = "";
                        if (accessTokenMap.containsKey("errcode")) {
                            errcode = accessTokenMap.get("errcode");
                        }
                        String errmsg = "";
                        if (accessTokenMap.containsKey("errmsg")) {
                            errmsg = accessTokenMap.get("errmsg");
                        }
                        tmpRet[1] = StringHelper.joinNotEmpty("-", errcode, errmsg);
                    }
                } else {
                    tmpRet[0] = "-1";
                    tmpRet[1] = accessTokenMap.get("RespInfo");
                }
            } finally {
                bankFront.isGettingToken = false;
            }
        }

        return tmpRet;

    }

    /**
     * AES使用128位的 AES-CBC模式对请求数据进行加密
     *
     * @param orginal 待加密字符串
     * @param aesKey  加密密钥
     * @param
     * @return
     * @throws
     */
    @SuppressWarnings("static-access")
    public String encrypt(String orginal, String aesKey) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec skeySpec = new SecretKeySpec(aesKey.getBytes("UTF-8"), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec("0000000000000000".getBytes("UTF-8")));
            byte[] encrypted = cipher.doFinal(orginal.getBytes("UTF-8"));

            return new Base64().encodeBase64String(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
            WriteBankLogLn(ex);
        }
        return null;
    }

    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, String signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals("sign")) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空，则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if ("MD5".equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if ("HMAC-SHA256".equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }
    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    /**
     * 生成 HMACSHA256
     * @param data 待处理数据
     * @param key 密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 返回HTTP字符串转MAP
     *
     * @param
     * @return
     */
    public static Map<String,String> convertToMap(List<String> arrayParam) {
        Map<String,String> params = new HashMap<String,String>();

         Iterator it = arrayParam.iterator();
         while(it.hasNext()){
             String node = it.next().toString();//mch_appid=43124123
             if (StringHelper.isNullOrEmpty(node)) {
                 continue;
             }
             int index = node.indexOf("=");//9
             String key = node.substring(0, index);//mch_appid
             String value = node.substring(index+1);//43124123
             params.put(key, value);
             params.get("merch_id");
         }

        return params;
    }

    public static Map<String, String> getValue(String param) {
        Map map = new HashMap();
        String str = "";
        String key = "";
        Object value = "";
        char[] charList = param.toCharArray();
        boolean valueBegin = false;
        for (int i = 0; i < charList.length; i++) {
            char c = charList[i];
            if (c == '{') {
                if (valueBegin == true) {
                    value = getValue(param.substring(i, param.length()));
                    i = param.indexOf('}', i) + 1;
                    map.put(key, value);
                }
            } else if (c == '=') {
                valueBegin = true;
                key = str;
                str = "";
            } else if (c == ',') {
                valueBegin = false;
                value = str;
                str = "";
                map.put(key, value);
            } else if (c == '}') {
                if (str != "") {
                    value = str;
                }
                map.put(key, value);
                return map;
            } else if (c != ' ') {
                str += c;
            }
        }
        return map;
    }
    
    /**通过微信接口获取微信侧公钥
     * @throws Exception */
    protected PublicKey getPublicKeyFromWeiXin(ActSetItem pActItem) throws Exception{
    	ArrayList<String> arrayParam = new ArrayList<String>();
        StringBuilder sbXml = new StringBuilder();
        
        sbXml.append("<xml>\r\n");
        arrayParam.add("mch_id=" + pActItem.merchantId);
        sbXml.append("  <mch_id>" + pActItem.merchantId + "</mch_id>\r\n");
        
        String nonce_str = get_nonce_str();
        arrayParam.add("nonce_str=" + nonce_str);
        sbXml.append("  <nonce_str>" + nonce_str + "</nonce_str>\r\n");
        
        String[] signParams = new String[arrayParam.size()];
        arrayParam.toArray(signParams);
        String sign = signMessage(signParams, pActItem.saltKey);
        sbXml.append("  <sign>" + sign + "</sign>\r\n");
        sbXml.append("</xml>");
        
        String[] tmpStrRet = sendToBank(sbXml.toString(), "getpublickey", pActItem);
        if (tmpStrRet[0].length() == 0) {
            StringBuilder sbMsg = new StringBuilder();
            XmlTextReader tmpBkReader = loadBankRespXmlReader(tmpStrRet[1], sbMsg);
            if (tmpBkReader != null) {
                String return_code = tmpBkReader.getFirstText("/xml/return_code");
                if ("SUCCESS".equals(return_code)) {
                	String result_code = tmpBkReader.getFirstText("/xml/result_code");
                	if("SUCCESS".equals(result_code)){
                		String pub_key = tmpBkReader.getFirstText("/xml/pub_key");
                		String pickKey = StringHelper.pickupStr(pub_key, "-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----");
                		pickKey = pickKey.trim();
                		if(pickKey.length()>0){
                			byte[] keyBytes = Base64.decodeBase64(pickKey);
                			KeyFactory factory = KeyFactory.getInstance("RSA");
                	        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
                	        PublicKey key = factory.generatePublic(x509EncodedKeySpec);
                	        return key;
                		}else{
                			throw new Exception("获取对方公钥失败：返回KEY内容为空");
                		}
                	}else{
                		throw new Exception("获取对方公钥失败："+StringHelper.joinNotEmpty("-", tmpBkReader.getFirstText("/xml/err_code"),tmpBkReader.getFirstText("/xml/err_code_des")));
                	}
                }else{
                	throw new Exception("获取对方公钥失败："+ tmpBkReader.getFirstText("/xml/return_msg"));
                }
            }else{
            	throw new Exception("获取对方公钥失败："+sbMsg.toString());
            }
        }else{
        	throw new Exception("获取对方公钥失败："+StringHelper.joinNotEmpty("-", tmpStrRet));
        }
    }

    public PublicKey getPublicKey(String rsaFilePath) throws Exception {
    	byte keyDate[] = null;
    	if(rsaFilePath.toLowerCase().endsWith(".p12")){
    		keyDate = FileHelper.readFileBytes(rsaFilePath);
    	}else{
    		StringBuilder stringBuilder = new StringBuilder();
            @SuppressWarnings("resource")
            BufferedReader reader = new BufferedReader(new FileReader(rsaFilePath));
            String readLine = reader.readLine();
            while (readLine != null) {
                if (readLine.charAt(0) != '-') {
                    stringBuilder.append(readLine);
                }
                readLine = reader.readLine();
            }
            
            if (Base64.isBase64(stringBuilder.toString())) {
                keyDate = Base64.decodeBase64(stringBuilder.toString());
            } else {
                keyDate = FileHelper.readFileBytes(rsaFilePath);
            }
    	}
        
        KeyFactory factory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyDate);
        PublicKey key = factory.generatePublic(x509EncodedKeySpec);
        return key;
    }

    public String encryptData(PublicKey key, String plain) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] date = cipher.doFinal(plain.getBytes("utf-8"));
        return Base64.encodeBase64String(date);
    }

    protected String[] sendPost(String p_reqStr, String tmpUrl) throws UnsupportedEncodingException {
        String[] retStr = new String[]{"",""};

        WriteBankLogLn("2.提交银行的地址：" + tmpUrl);
        WriteBankLogStep2(p_reqStr);
        WebRequest tmpWebRequest = new WebRequest(tmpUrl);
        tmpWebRequest.setConnectTimeout(100000);
        tmpWebRequest.setRequestProperty("Content-Type", "text/xml");
        tmpWebRequest.setRequestProperty("connection", "Close");
        byte[] tmpUpBytes = p_reqStr.getBytes(getCharset());
        retStr = tmpWebRequest.upload(tmpUpBytes, getCharset());
        WriteBankLogStep3(retStr[1]);
        return retStr;
    }
}